using System;
using System.Text;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace CustomControls
{
   public partial class TrayAnimation : Component
   {
      int FLoopCount;
      int FLoopTimes;
      int FFrameCount;
      int FStartFrame;
      int FCurrentFrame;
      int FBalloonTimeout;
      bool FBalloonVisible;

      object FTag;
      bool FEnabled = true;
      int FAnimationInterval;

      ContextMenu FMenu;
      bool FIsMenuVisible;
      Control FParentControl;
      IconList FCurrentAnimation;
      Point FMenuPosition = new Point();
      IconListKeyedCollection FAnimationSequenceList = new IconListKeyedCollection();


      IconList CurrentAnimation
      {
         get
         {
            return FCurrentAnimation;
         }
         set
         {
            if (FCurrentAnimation == value) return;

            FCurrentAnimation = value;

            FCurrentFrame = 0;
         }
      }

      private void Initialize()
      {
         InitializeComponent();

         ComposeAnimationSequenceList();

         FAnimationInterval = timerAnimation.Interval;
      }
      private void ComposeAnimationSequenceList()
      {
         foreach (IconList list in FAnimationSequenceList)
         {
            foreach (Icon icon in list) icon.Dispose();
            
            list.Clear();
         }

         FAnimationSequenceList.Clear();

         StringCollection keys = imageListAnimationSequences.Images.Keys;

         for (int i = 0; i < imageListAnimationSequences.Images.Count; i++)
         {
            AddImage(imageListAnimationSequences.Images[i], keys[i], false);
         }
      }

      public TrayAnimation()
      {
         Initialize();
      }
      public TrayAnimation(Image image, string tip, bool show)
      {
         Initialize();

         Tip = tip;

         AddImage(image, "", show);
      }

      public void Play()
      {
         Play("", -1, -1, -1, FAnimationInterval);
      }
      public void Show()
      {
         Show("", FCurrentFrame);
      }
      public void Pause()
      {
         timerAnimation.Stop();

         if (Visible) Show("", -1);
      }
      public void ShowBalloonTip()
      {
         Show();
         notifyIcon.ShowBalloonTip(30);
      }
      public void Pause(int frame)
      {
         timerAnimation.Stop();

         if (Visible) Show("", frame);
      }
      public void Play(string animationName)
      {
         Play(animationName, -1, -1, -1, FAnimationInterval);
      }
      public void Show(string animationName, int frame)
      {
         IconList animation = string.IsNullOrEmpty(animationName) ? CurrentAnimation : FAnimationSequenceList.GetItemByKey(animationName);

         if (animation == null || animation.Count == 0) return;

         if (frame < 0 || frame >= animation.Count) frame = FCurrentFrame;

         CurrentAnimation = animation;

         notifyIcon.Icon = CurrentAnimation[frame];

         notifyIcon.Visible = true;
      }
      public void AddImage(Image image, string animationName, bool show)
      {
         if (string.IsNullOrEmpty(animationName)) animationName = "<Default>";

         IconList animation = FAnimationSequenceList.GetItemByKey(animationName);

         if(animation == null)
         {
            animation = new IconList(animationName);
            FAnimationSequenceList.Add(animation);
            CurrentAnimation = animation;
         }

         animation.Add(Icon.FromHandle(((Bitmap)image).GetHicon()));

         if (show) Show("", CurrentAnimation.Count - 1);
      }
      public void SetBalloonTip(string title, string tip, ToolTipIcon icon)
      {
         notifyIcon.BalloonTipTitle = title;
         notifyIcon.BalloonTipText = tip;
         notifyIcon.BalloonTipIcon = icon;
      }
      public void ShowBalloonTip(int frame, string title, string tip, int timeout)
      {
         Show("", frame);
         notifyIcon.ShowBalloonTip(timeout, title, tip, ToolTipIcon.Info);
      }
      public void Play(string animationName, int startFrame, int frameCount, int loopTimes, int animationInterval)
      {
         timerAnimation.Enabled = false;

         IconList icons = FAnimationSequenceList.GetItemByKey(animationName);
         if (icons != null) CurrentAnimation = icons;

         FStartFrame = (startFrame > 0 && startFrame < CurrentAnimation.Count) ? startFrame : 0;

         Show("", startFrame);
         FLoopTimes = loopTimes;
         FCurrentFrame = startFrame > 0 ? startFrame : 0;
         FAnimationInterval = animationInterval > 0 ? animationInterval : 50;
         FFrameCount = frameCount > 0 ? (FStartFrame + frameCount < CurrentAnimation.Count ? frameCount : CurrentAnimation.Count) : CurrentAnimation.Count;

         timerAnimation.Interval = FAnimationInterval;
         if (FFrameCount > 1) timerAnimation.Enabled = true;
      }
      public void Hide()
      {
         notifyIcon.Visible = false;
      }
      public void Stop()
      {
         Hide();
         FCurrentFrame = 0;
         timerAnimation.Stop();
      }

      public void ShowMenu()
      {
         if (FMenu == null || ParentControl == null) return;

         int left = ParentControl.Left;
         bool visible = ParentControl.Visible;

         if (!visible)
         {
            ParentControl.Left = Screen.PrimaryScreen.WorkingArea.Width;
            ParentControl.Visible = true;
         }         
         
         FMenuPosition.X = Cursor.Position.X - FParentControl.Location.X;
         FMenuPosition.Y = Cursor.Position.Y - FParentControl.Location.Y;

         FIsMenuVisible = true;
         FMenu.Show(ParentControl, FMenuPosition, LeftRightAlignment.Left);
         FIsMenuVisible = false;

         ParentControl.Visible = visible;
         ParentControl.Left = left;
      }

      public object Tag
      {
         get
         {
            return FTag;
         }
         set
         {
            FTag = value;
         }
      }
      public string Tip
      {
         get
         {
            return string.IsNullOrEmpty(notifyIcon.Text) ? "" : notifyIcon.Text;
         }
         set
         {
            notifyIcon.Text = string.IsNullOrEmpty(value) ? "" : value.Substring(0, value.Length < 64 ? value.Length : 63);
         }
      }
      public bool Visible
      {
         get
         {
            return notifyIcon.Visible;
         }
         set
         {
            if (value) Show(); else Hide();
         }
      }
      public bool Enabled
      {
         get
         {
            return FEnabled;
         }
         set
         {
            FEnabled = value;
         }
      }
      public ContextMenu Menu
      {
         get
         {
            return FMenu;
         }
         set
         {
            FMenu = value;
         }
      }
      public bool IsMenuVisible
      {
         get
         {
            return FIsMenuVisible;
         }
      }
      public Control ParentControl
      {
         get
         {
            return FParentControl;
         }
         set
         {
            FParentControl = value;
         }
      }
      public bool IsBalloonVisible
      {
         get
         {
            return FBalloonVisible;
         }
      }
      public string CurrentAnimationName
      {
         get
         {
            return CurrentAnimation.Name;
         }
      }
      public ImageList AnimationSequences
      {
         get
         {
            return imageListAnimationSequences;
         }
         set
         {
            imageListAnimationSequences.Dispose();

            imageListAnimationSequences = value;

            ComposeAnimationSequenceList();
         }
      }

      public event EventHandler Click;
      public event EventHandler DoubleClick;
      public event EventHandler BalloonShow;
      public event EventHandler BalloonHide;
      public event EventHandler BalloonClick;
      public event MouseEventHandler MouseUp;
      public event MouseEventHandler MouseDown;
      public event MouseEventHandler MouseMove;
      public event MouseEventHandler MouseClick;
      public event MouseEventHandler MouseDoubleClick;

      private void notifyIcon_BalloonTipClicked(object sender, EventArgs e)
      {
         if (FEnabled && BalloonClick != null) BalloonClick(this, e);
      }
      private void notifyIcon_BalloonTipClosed(object sender, EventArgs e)
      {
         FBalloonVisible = false;
         if (FEnabled && BalloonHide != null) BalloonHide(this, e);
      }
      private void notifyIcon_BalloonTipShown(object sender, EventArgs e)
      {
         FBalloonVisible = true;
         if (FEnabled && BalloonShow != null) BalloonShow(this, e);
      }
      private void notifyIcon_Click(object sender, EventArgs e)
      {
         if (FEnabled && Click != null) Click(this, e);
      }
      private void notifyIcon_DoubleClick(object sender, EventArgs e)
      {
         if (FEnabled && DoubleClick != null) DoubleClick(this, e);
      }
      private void notifyIcon_MouseClick(object sender, MouseEventArgs e)
      {
         if (FEnabled && MouseClick != null) MouseClick(this, e);
      }
      private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
      {
         if (FEnabled && MouseDoubleClick != null) MouseDoubleClick(this, e);
      }
      private void notifyIcon_MouseDown(object sender, MouseEventArgs e)
      {
         if (FEnabled && MouseDown != null) MouseDown(this, e);
      }
      private void notifyIcon_MouseMove(object sender, MouseEventArgs e)
      {
         if (FEnabled && MouseMove != null) MouseMove(this, e);
      }
      private void notifyIcon_MouseUp(object sender, MouseEventArgs e)
      {
         if (FEnabled && MouseUp != null) MouseUp(this, e);
      }

      private void timerAnimation_Tick(object sender, EventArgs e)
      {
         if (++FCurrentFrame >= FStartFrame + FFrameCount)
         {
            FLoopCount++;
            FCurrentFrame = FStartFrame;
            if (FLoopTimes >= 0 && FLoopCount >= FLoopTimes)
            {
               FLoopCount = 0;
               Pause(FStartFrame);
            }
         }

         Show();

         if (CurrentAnimation.Count <= 1) timerAnimation.Enabled = false;
      }
   }

   class IconList : List<Icon>
   {
      public string Name;

      public IconList(string name)
      {
         Name = name;
      }
   }

   class IconListKeyedCollection : KeyedCollection<string, IconList>
   {
      public IconListKeyedCollection()
         : base()
      {

      }

      protected override string GetKeyForItem(IconList item)
      {
         return item.Name;
      }

      public IconList GetItemByKey(string key)
      {
         IconList item = null;
         if (Dictionary != null) Dictionary.TryGetValue(key, out item);
         return item;
      }
   }
}